package org.visionlibrary.image.filters.dithering;

import javax.media.jai.TiledImage;

import org.visionlibrary.image.acquisition.ImageFactory;
import org.visionlibrary.image.model.AbstractFilter;


public class FalseFloydSteinberg extends AbstractFilter {
	protected int shades = 3;

	protected float errors[];

	public TiledImage applyFilter(TiledImage src, TiledImage dest) {
		if (null == src)
			throw new NullPointerException("Source image is null.");

		if (null == dest)
			dest = (new ImageFactory()).createCompatibleImage(src);
		
		int maxX = src.getWidth();
		int maxY = src.getHeight();
		errors = new float[src.getHeight() * src.getWidth()];

		if (shades < 2)
			shades = 2;
		float factor = (float) (shades - 1) / (float) 255;

		for (int ch = 0; ch < src.getNumBands(); ch++) {
			for (int x = 0; x < maxX; x++)
				for (int y = 0; y < maxY; y++) {
					float newval = src.getSample(x, y, ch)
							+ errors[y * src.getHeight() + x];

					newval *= factor;
					newval = (float) (Math.floor(newval + 0.5));
					newval /= factor;

					if (newval > 255)
						newval = 255;
					if (newval < 0)
						newval = 0;

					dest.setSample(x, y, ch, (int) (newval + 0.5));

					float cerror = src.getSample(x, y, ch)
							+ errors[y * src.getHeight() + x]
							- dest.getSample(x, y, ch);

					if (x < maxX - 1 && y < maxY - 1) {
						errors[y * src.getHeight() + x + 1] += cerror * 3.0f / 8.0f;
						errors[(y + 1) * src.getHeight() + x] += cerror * 3.0f / 8.0f;
						errors[(y + 1) * src.getHeight() + x + 1] += cerror * 2.0f / 8.0f;
					}
				}
		}

		return dest;
	}

	public int getShades() {
		return shades;
	}

	public void setShades(int shades) {
		this.shades = shades;
	}
}
